package com.newjiabo.common.ski.media.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.newjiabo.common.ski.config.WeChatConfig;
import com.newjiabo.common.ski.media.module.entity.dto.WeChatPayDto;
import com.newjiabo.common.utils.StringUtils;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 微信支付工具类
 *
 * @author CaoMengHui.
 */
@Component
public class WxPayUtil {
    /**
     * 商户证书私钥-apiclient_key.pem路径
     */
    @Value("${wechat.apiclientKey}")
    private String apiclientKey;
    @Value("${wechat.mchId}")
    private String mchId;
    @Value("${wechat.apiV3Key}")
    private String apiV3Key;
    @Value("${wechat.appid}")
    private String appid;
    @Value("${wechat.wechatPayCert}")
    private String wechatPayCert;
    @Value("${wechat.secret}")
    private String secret;
    @Value("${wechat.notifyUrl}")
    private String notifyUrl;
    /**
     * 商户证书序列号
     */
    @Value("${wechat.wechatPaySerial}")
    private String wechatPaySerial;

    public static final String JSAPI_V3_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    public static final String REFUNDS_V3_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    private static final Logger log = LoggerFactory.getLogger(WxPayUtil.class);

    private static String nowTimeStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
    /**
     * 生成订单号
     * 以年月日时分秒带6位随机值
     */
    public static String buildOrderNo() {
        return StringUtils.join("O",nowTimeStr, RandomStringUtils.randomNumeric(6));
    }


    /**
     * 生成退款订单号
     *
     * @return
     * @author CaoMengHui.
     * @date 2023/9/23 17:28
     */
    public static String buildRefundNo() {
        return StringUtils.join("A",nowTimeStr, RandomStringUtils.randomNumeric(6));
    }


    /**
     * 元转为分
     *
     * @return
     */
    public static Integer YUANConvertToFen(Double total) {
        total = total * 100;
        return total.intValue();
    }

    /**
     * 元转为分
     *
     * @return
     */
    public static Integer YUANConvertToFen(String total) {
        Double s = Double.valueOf(total) * 100;
        return s.intValue();
    }

    /**
     * v3 异步通知验证签名
     *
     * @param body 异步通知密文
     * @param key  api 密钥
     * @return 异步通知明文
     * @throws Exception 异常信息
     */
    public static String verifyNotify(String body, String key) throws Exception {
        // 获取平台证书序列号
        JSONObject resultObject = JSON.parseObject(body);
        JSONObject resource = resultObject.getJSONObject("resource");
        String cipherText = resource.getString("ciphertext");
        String nonceStr = resource.getString("nonce");
        String associatedData = resource.getString("associated_data");
        AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
        // 密文解密
        return aesUtil.decryptToString(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonceStr.getBytes(StandardCharsets.UTF_8),
                cipherText
        );
    }

    /**
     * 获取微信回调结果
     *
     * @param request
     * @return
     * @throws Exception
     */
    public JSONObject getCallback(HttpServletRequest request) throws Exception {
        //获取报文
        StringBuffer stringBuffer = new StringBuffer();
        ServletInputStream inputStream = request.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuffer.append(line);
        }
        String body = stringBuffer.toString();

        //随机串
        String nonce = request.getHeader("Wechatpay-Nonce");
        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");
        //证书序列号（微信平台）
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");
        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 验签
        NotificationRequest notificationRequest = new NotificationRequest.Builder()
                .withSerialNumber(wechatPaySerial)
                .withNonce(nonce)
                .withTimestamp(timestamp)
                .withSignature(signature)
                .withBody(body)
                .build();
        NotificationHandler handler = new NotificationHandler(verifier(), apiV3Key.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(notificationRequest);
        return JSON.parseObject(notification.getDecryptData());
    }

    /**
     * 微信敏感数据加密公钥
     * 使用方法：
     * X509Certificate x509Certificate = getSaveCertificates();
     * RsaCryptoUtil.encryptOAEP(要加密的数据, x509Certificate)
     *
     * @return
     */
    public X509Certificate getSaveCertificates() {
        File file2 = new File(wechatPayCert);
        InputStream platformCertInputStream = FileUtil.getInputStream(file2);
        return PemUtil.loadCertificate(platformCertInputStream);
    }


    /**
     * 加载商户私钥
     */
    private PrivateKey getPrivateKeyByStr(String privateKey) {
        try {
            //示例：私钥为String字符串
            return PemUtil.loadPrivateKey(
                    new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));
        } catch (Exception e) {
            throw new RuntimeException("私钥文件不存在", e);
        }
    }

    /**
     * 构造签名串
     *
     * @param packageStr 待签名的参数
     * @return 构造后带待签名串
     */
    public String buildSignMessage(String packageStr, String nonceStr, String timestamp) {
        List<String> signMessage = new ArrayList<String>();
        signMessage.add(appid);
        signMessage.add(timestamp);
        signMessage.add(nonceStr);
        signMessage.add(packageStr);
        if (signMessage == null || signMessage.size() <= 0) {
            return null;
        }
        StringBuilder sbf = new StringBuilder();
        for (String str : signMessage) {
            sbf.append(str).append("\n");
        }
        return sbf.toString();
    }

    /**
     * 生成签名
     *
     * @param message
     * @return
     * @throws Exception
     */
    public String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        // 商户证书私钥
        sign.initSign(getPrivateKey());
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 获取商户号为merchantId的验签器
     *
     * @return Verifier
     */
    public Verifier getVerifier() throws Exception {
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey();
        //私钥签名对象
        PrivateKeySigner keySigner = new PrivateKeySigner(wechatPaySerial, privateKey);
        //身份认证对象
        WechatPay2Credentials pay2Credentials = new WechatPay2Credentials(mchId, keySigner);
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, pay2Credentials,
                //对称加密的秘钥
                apiV3Key.getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号，可继续调用putMerchant添加商户信息
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);
        return verifier;
    }

    /**
     * 获得验签器
     *
     * @return
     * @throws Exception
     */
    public Verifier verifier() throws Exception {
        //获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, new PrivateKeySigner(wechatPaySerial, getPrivateKey())), apiV3Key.getBytes(StandardCharsets.UTF_8));
        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);
        return verifier;
    }

    /**
     * 获取http请求对象
     * 每次请求更新平台证书
     *
     * @param verifier
     * @return
     */
    public CloseableHttpClient getWxPayClient(Verifier verifier) {
        //商户私钥
        PrivateKey privateKey = getPrivateKeyByStr(apiclientKey);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, wechatPaySerial, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }


    /**
     * 微信通讯client
     *
     * @return CloseableHttpClient
     */
    public CloseableHttpClient getClient() {
        /**
         * 商户证书私钥文件
         *
         * */
        ClassPathResource apiclientKeyFile = new ClassPathResource(apiclientKey);
        InputStream apiclientKeyInputStream = apiclientKeyFile.getStream();

        /**
         * 微信支付平台证书文件
         *
         * */
        ClassPathResource wechatPayCertFile = new ClassPathResource(wechatPayCert);
        InputStream wechatPayCertInputStream = wechatPayCertFile.getStream();

        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyInputStream);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, wechatPaySerial, merchantPrivateKey)
                .withWechatPay(Collections.singletonList(PemUtil.loadCertificate(wechatPayCertInputStream)));
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }

    /**
     * 获取商户证书私钥
     *
     * @return 私钥对象
     */
    public PrivateKey getPrivateKey() throws IOException {
        ClassPathResource apiclientKeyFile = new ClassPathResource(apiclientKey);
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyFile.getStream());
        return merchantPrivateKey;
    }


    /**
     * @param weChatPayDto
     * @return
     * @author CaoMengHui.
     * @date 2023/9/22 16:51
     */
    public CloseableHttpResponse getPrepayInfo(WeChatPayDto weChatPayDto) throws IOException {
        weChatPayDto.setAppid(appid);
        weChatPayDto.setMchid(mchId);
        weChatPayDto.setNotify_url(notifyUrl);
        CloseableHttpClient httpClient = this.getClient();
        HttpPost httpPost = new HttpPost(JSAPI_V3_URL);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=UTF-8");
        String weChatPayDtoJson = JSONObject.toJSONString(weChatPayDto);
        StringEntity weChatPayDtoEntity = new StringEntity(weChatPayDtoJson, "UTF-8");
        weChatPayDtoEntity.setContentType("application/json");
        httpPost.setEntity(weChatPayDtoEntity);
        CloseableHttpResponse result = httpClient.execute(httpPost);
        return result;
    }

    public CloseableHttpResponse getRefundInfo(String transactionId,String outRefundNo,Double refund,Double total) throws IOException {
        JSONObject refundsJson = new JSONObject();
        refundsJson.put("transaction_id", transactionId);
        refundsJson.put("out_refund_no", outRefundNo);
        JSONObject amount = new JSONObject();
        amount.put("refund",WxPayUtil.YUANConvertToFen(refund));
        amount.put("total",WxPayUtil.YUANConvertToFen(total));
        amount.put("currency","CNY");
        refundsJson.put("amount",amount);
        refundsJson.put("notify_url", WeChatConfig.getRefundNotifyUrl());
        String body = refundsJson.toJSONString();

        CloseableHttpClient client = this.getClient();
        HttpPost httpPost = new HttpPost(REFUNDS_V3_URL);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=UTF-8");

        StringEntity weChatPayDtoEntity = new StringEntity(body, "UTF-8");
        weChatPayDtoEntity.setContentType("application/json");
        httpPost.setEntity(weChatPayDtoEntity);
        CloseableHttpResponse result = client.execute(httpPost);
        return result;
    }
}

